from typing import List

from pandas import DataFrame, Series

from addr2elfSym import load_sym_ls__cache, bi_find_sym, Sym

import pandas


def __make_empty_row(logDf:DataFrame, indexK):
    logDf.loc[indexK, 'funcName'] = ''
    logDf.loc[indexK, 'funcAddr'] = 0
    logDf.loc[indexK, 'funcSize'] = 0
    logDf.loc[indexK, 'offsetInFunc'] = 0
    return


def bi_find_sym_then_updateRow(seg:'SegInElf', syms:List[Sym], logDf:DataFrame, indexK, rowK:Series)->None:
    if seg is None or syms is None or len(syms) == 0:
        __make_empty_row(logDf,indexK)
        return

    # callerAddr:int=int(rowK['callerAddr'], base=16)
    callerAddr: int = rowK['callerAddr'] - seg.dlpi_addr

    # callerAddr: int = 0x98ac4a5
    func1:Sym=bi_find_sym(syms, callerAddr)

    if func1 is None :
        __make_empty_row(logDf,indexK)
        return

    # addr:int=0x98ac4a5



    logDf.loc[indexK, 'callerAddr'] = f"0x{rowK['callerAddr']:0x}"
    #写入相对地址
    logDf.loc[indexK, 'callerAddr_relative'] = f"0x{callerAddr:0x}"
    logDf.loc[indexK, 'funcName'] = func1.symbol_name
    logDf.loc[indexK, 'funcAddr'] = f"0x{func1.symbol_addr:0x}"
    logDf.loc[indexK, 'funcSize'] = int(func1.symbol_size)
    logDf.loc[indexK, 'offsetInFunc'] = func1.offsetInFunc(callerAddr)

    logDf.loc[indexK, 'elfFilePath'] = seg.elfFilePath()


    return


class SegInElf:
    def __init__(self,dlpi_name:str,dlpi_addr:int,p_vaddr:int,p_filesz:int):
        # //地址范围: 起点为 dlpi_addr+p_vaddr, 长度为 p_filesz 的地址范围
        self.dlpi_name:str=dlpi_name
        self.dlpi_addr:int=dlpi_addr
        self.p_vaddr:int=p_vaddr
        self.p_filesz:int=p_filesz

    def elfFilePath(self):
        return self.dlpi_name

    def containAddr(self,addr:int)->bool:
        # //地址范围: 起点为 dlpi_addr+p_vaddr, 长度为 p_filesz 的地址范围
        startAddr:int=self.dlpi_addr+self.p_vaddr
        endAddr:int=startAddr+self.p_filesz
        contain:bool=addr >= startAddr and addr <= endAddr
        return contain

    def __str__(self):
        return f"{self.dlpi_name},0x{self.dlpi_addr:0x},0x{self.p_vaddr:0x},0x{self.p_filesz:0x}"

    @staticmethod
    def fromCsvFile(soLsCsvFP:str,elfFilePath:str)->List['SegInElf']:
        soLsCsv: DataFrame = pandas.read_csv(filepath_or_buffer=soLsCsvFP)

        soLsCsv['dlpi_name']=soLsCsv['dlpi_name'].fillna('')
        #路径为空的so，表示的是当前主程序文件
        soLsCsv['dlpi_name'] = soLsCsv['dlpi_name'].apply(lambda x: elfFilePath if x is None or len(x)==0 else x ).astype(str)

        soLsCsv['dlpi_addr'] = soLsCsv['dlpi_addr'].apply(lambda x: int(x,base=16))
        soLsCsv['p_vaddr'] = soLsCsv['p_vaddr'].apply(lambda x: int(x,base=16)).astype(int)
        soLsCsv['p_filesz'] = soLsCsv['p_filesz'].apply(lambda x: int(x,base=16)).astype(int)

        segLs:List['SegInElf']=[]
        for index, row in soLsCsv.iterrows():
            seg=SegInElf(row["dlpi_name"], row["dlpi_addr"], row["p_vaddr"], row["p_filesz"])
            segLs.append(seg)

        return segLs

    @staticmethod
    def addrInWhichSeg(segLs:List['SegInElf'], addr:int)->'SegInElf':
        # //地址范围: 起点为 dlpi_addr+p_vaddr, 长度为 p_filesz 的地址范围
        for segK in segLs:
            if segK.containAddr(addr):
                return segK

        return None




def doCsvUpdate(elfFilePath:str, csvFilePath: str, soLsCsvFP:str):
    # csvFilePath: str = "/pubx/hook_mem_alloc/_MemAllocLog_clang_1684834385.out"
    # soLsCsvFP:str = "/app/build-llvm15/_soList_clang-15_20230525_000728.out"

    segLs:List['SegInElf']=SegInElf.fromCsvFile(soLsCsvFP,elfFilePath)

    csvFileNew: str = f"{csvFilePath}.new"
    print(f"load csvFilePath: {csvFilePath}")
    logDf: DataFrame = pandas.read_csv(filepath_or_buffer=csvFilePath)

    logDf['callerAddr'] = logDf['callerAddr'].apply(lambda x:int(x,base=16) if x!='(nil)' else 0)

    # elfFilePath="/pubx/build-llvm15/bin/clang-15"
    # elfFilePath="/usr/bin/ls"
    # syms:List[Sym]=load_sym_ls(elfFilePath)

    print("loop : bi find symbol then update csv row")

    for index, row in logDf.iterrows():
        addr:int=row['callerAddr']
        if addr ==0:
            print(f"skip callerAddr {addr}")
            continue
        seg:SegInElf=SegInElf.addrInWhichSeg(segLs,addr)
        syms: List[Sym]=None
        if seg is not None:
            syms:List[Sym]=load_sym_ls__cache(seg.elfFilePath())
        bi_find_sym_then_updateRow(seg,syms, logDf, index, row)
        # logDf.loc[index, 'newColumn'] = np.random.randint(0, 10)



    # logDf[ 'funcAddr'] = logDf[  'funcAddr'].astype(int)

    logDf['funcSize']=logDf['funcSize'].fillna(0)
    logDf[ 'funcSize'] = logDf[  'funcSize'].astype(int)

    logDf['offsetInFunc']=logDf['offsetInFunc'].fillna(0)
    logDf[ 'offsetInFunc'] = logDf[  'offsetInFunc'].astype(int)

    logDf.to_csv(path_or_buf=csvFileNew, index=False)

    print(f"csvFileNew:{csvFileNew}")

import sys
if __name__=="__main__":
    if len(sys.argv)<3:
        print(f"usage:{__file__} elfFilePath csvFilePath")
        exit(5)

    # csvFilePath: str = "/pubx/hook_mem_alloc/_MemAllocLog_clang_1684834385.out"
    # elfFilePath="/pubx/build-llvm15/bin/clang-15"
    # elfFilePath="/usr/bin/ls"


    elfFilePath: str = sys.argv[1]
    csvFilePath:str=sys.argv[2]
    soLsCsvFP:str=sys.argv[3]
    doCsvUpdate(elfFilePath,csvFilePath,soLsCsvFP)

    #addr2elfSym.py /pubx/build-llvm15/bin/clang-15 /pubx/hook_mem_alloc/_MemAllocLog_clang_1684834385.out

    #cp /pubx/hook_mem_alloc/mini_main_c.txt /tmp/mini_main.c
    #LD_PRELOAD=/pubx/hook_mem_alloc/libHookMemAlloc.so /app/build-llvm15/bin/clang-15 -c /tmp/mini_main.c
    #su - root #建议到docker中, 切换到root.  由于 形如 路径 /lib/x86_64-linux-gnu/libstdc++.so.6.pickle 显然需要root权限才能写入, 所以需要切换到root, 在真机下这有风险（可能会不小心破坏了操作系统）
    #python /pubx/hook_mem_alloc/addr2func/addrCsv2elfSym.py   /app/build-llvm15/bin/clang-15 /app/build-llvm15/_MemAllocLog_clang-15_20230525_012400.out /app/build-llvm15/_soList_clang-15_20230525_012400.out
    #结果文件 /app/build-llvm15/_MemAllocLog_clang-15_20230525_012400.out.new 内容如下(基本ok了):
    """
func,cnt,memSize,memAddr,memAddrOld,callerAddr,timespecCurrent.tv_sec,timespecCurrent.tv_nsec,callerAddr_relative,funcName,funcAddr,funcSize,offsetInFunc,elfFilePath
a,1,72704,0x79582a0,(nil),0x7f83bfaaa97a,1684949040,391429194,0xaa97a,_ZSt19__throw_ios_failurePKci,0xa5d34,204,19526,/lib/x86_64-linux-gnu/libstdc++.so.6
a,1,352,0x7969eb0,(nil),0x7f83bfaae9cc,1684949040,391540015,0xae9cc,_Znwm,0xae9b0,53,28,/lib/x86_64-linux-gnu/libstdc++.so.6
a,1,160,0x796a020,(nil),0x7f83bfaae9cc,1684949040,391540927,0xae9cc,_Znwm,0xae9b0,53,28,/lib/x86_64-linux-gnu/libstdc++.so.6
a,1,160,0x796a0d0,(nil),0x7f83bfaae9cc,1684949040,391549894,0xae9cc,_Znwm,0xae9b0,53,28,/lib/x86_64-linux-gnu/libstdc++.so.6
c,17,12,0x796a180,(nil),0x2b62a63,1684949040,391567679,0x2b62a63,_ZN4llvm13StringMapImpl15LookupBucketForENS_9StringRefE,0x2b629a0,454,195,/app/build-llvm15/bin/clang-15
...
    """

